#pragma once

#include "Sol9Types.h"
#include "Sol9Math.h"
#include "Sol9Util.h"
#include "dimension2d.h"
#include "position2d.h"

namespace sol9
{
namespace core
{

	template <typename value>
	class rect2d
	{
	public:
		union
		{
			struct
			{
				position2d<value> p1;
				position2d<value> p2;
			};

			struct
			{
				value left;
				value top;
				value right;
				value bottom;
			};
		};

	public:
		// constructors
		rect2d() : p1(0, 0), p2(0, 0) {}

		rect2d(const position2d<value>& _p1, const position2d<value>& _p2)
			: p1(_p1), p2(_p2) {}

		rect2d(value _left, value _top, value _right, value _bottom)
			: left(_left), top(_top), right(_right), bottom(_bottom) {}
		
		template<typename value2>
		rect2d(const position2d<value>& position, const dimension2d<value2>& size)
			: p1(position), p2(size + position) {}

		// operators
		// rect = rect
		rect2d<value>& operator=(const rect2d<value>& other)
		{
			p1 = other.p1;
			p2 = other.p2;
			return *this;
		}

		// rect + position
		rect2d<value> operator+(const position2d<value>& position) const
		{
			return rect2d<value>(p1 + position, p2 + position);
		}

		// rect += position
		rect2d<value>& operator+=(const position2d<value>& position)
		{
			p1 += position;
			p2 += position;
			return *this;
		}

		// rect - position
		rect2d<value> operator-(const position2d<value>& position) const
		{
			return rect2d<value>(p1 - position, p2 - position);
		}

		// rect -= position
		rect2d<value>& operator-=(const position2d<value>& position)
		{
			p1 -= position;
			p2 -= position;
			return *this;
		}

		// rect == rect
		bool operator==(const rect2d<value>& other) const
		{
			return core::equals(p1, other.p1)
				&& core::equals(p2, other.p2);
		}

		// rect != rect
		bool operator!=(const rect2d<value>& other) const
		{
			return !(*this == ohter);
		}

		// rect < rect
		bool operator<(const rect2d<value>& other) const
		{
			return getArea() < other.getArea();
		}

		// fuctions
		rect2d<value>& set(const position2d<value>& _p1, const position2d<value>& _p2)
		{
			p1 = _p1;
			p2 = _p2;
			return *this;
		}

		rect2d<value>& set(value _left, value _top, value _right, value _bottom)
		{
			left = _left;
			top = _top;
			right = _right;
			bottom = _bottom;
			return *this;
		}

		value getWidth() const
		{
			return right - left;
		}

		value getHeight() const
		{
			return bottom - top;
		}

		value getArea() const
		{
			return getWidth() * getHeight();
		}

		position2d<value> getCenter() const
		{
			return position2d<value>((left + right) / (value)2, (top + bottom) / (value)2);
		}

		dimension2d<value> getSize() const
		{
			return dimension2d<value>(getWidth(), getHeight());
		}

		void repair()
		{
			if (right < left)
				core::swap(right, left);

			if (bottom < top)
				core::swap(bottom, top);
		}

		void clipAgainst(const rect2d<value>& other)
		{
			if (left < other.left)
				left = other.left;

			if (top < other.top)
				top = other.top;

			if (right > other.right)
				right = other.right;

			if (bottom > other.bottom)
				bottom = other.bottom;

			// correct possible invalid rect resulting from clipping
			//if (left > right)
			//	left = right;

			//if (top > bottom)
			//	top = bottom;
		}

		bool isValid() const
		{
			return (right >= left) && (bottom >= top)
		}
		
		bool isPointInsided(const position2d<value>& position) const
		{
			return (left <= position.x) && (top <= position.y)
				&& (right >= position.x) && (botoom >= position.y);
		}

		bool isRectCollided(const rect2d<value>& other) const
		{
			return (left < other.right) && (right > other.left)
				&& (top < other.bottom) && (bottom > other.top);
		}
	};

	typedef rect2d<f32> rect2df;
	typedef rect2d<s32> rect2di;

} // namespace core
} // namespace sol9